﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Dou.Clock;
using Dou.IO;
using Dou.Sync;
using Dou.Timer;
using Dou.Utils;

namespace Dou.Net
{
    /// <summary>
    /// TCP Socket 连接类
    /// </summary>
    public class TCPSocketConnect
    {
        /// <summary>
        /// 套接字请求连接成功时的事件
        /// </summary>
        public event Action onConnectSuccess;

        /// <summary>
        /// 套接字连接请求超时时的事件
        /// </summary>
        public event Action onConnectTimeout;

        /// <summary>
        /// 套接字连接请求发生错误时的事件
        /// </summary>
        public event Action onConnectFailure;

        /// <summary>
        /// 套接字连接被远程服务端主动关闭时的事件
        /// </summary>
        public event Action onConnectClosed;

        /// <summary>
        /// 接收到数据包时的事件
        /// </summary>
        public event Action<byte[]> onReceiveData;

        /// <summary>
        /// 接收数据发生错误时的事件
        /// </summary>
        public event Action onReceiveError;

        /// <summary>
        /// 发送数据发生错误时的事件
        /// </summary>
        public event Action onSendError;

        protected Socket _socket;

        protected Endian _endian;
        protected Func<IPacketParser> _createPacketParser;
        
        protected IPacketParser _packetParser;
        protected ConcurrentQueue<byte[]> _sendQueue;

        protected string _ip;
        protected int _port;
        protected int _connectTimeoutID;

        public TCPSocketConnect(Endian endian, Func<IPacketParser> createPacketParser)
        {
            _endian = endian;
            _createPacketParser = createPacketParser;
            _packetParser = _createPacketParser();
            _sendQueue = new ConcurrentQueue<byte[]>();
        }

        /// <summary>
        /// 是否连接中
        /// </summary>
        public bool connected
        {
            get
            {
                if (_socket != null)
                {
                    return _socket.Connected;
                }
                return false;
            }
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        public void Connect(string ip, int port, float timeout = 3)
        {
            if(_socket != null)
            {
                Log.Warning($"[{_ip}:{_port}]连接已经存在，请先关闭连接！");
                return;
            }
            try
            {
                _ip = ip;
                _port = port;
                // 创建套接字对象并设定地址及接口
                IPEndPoint endPoint = new IPEndPoint(Dns.GetHostAddresses(_ip)[0], _port);
                _socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                // 发送缓冲区大小
                _socket.SendBufferSize = 1024 * 1024;
                // 超时判断
                _connectTimeoutID = TimerManager.SetTimeout(ClockTimeType.RealTime, timeout, OnConnectTimeout);
                // 开始连接
                _socket.BeginConnect(endPoint, ar =>
                {
                    MainThreadContext.Post(OnConnectComplete);
                }, null);
            }
            catch (Exception e)
            {
                Log.Error($"[{_ip}:{_port}]连接失败：" + e);
                onConnectFailure?.Invoke();
            }
        }

        private void OnConnectTimeout(object[] args)
        {
            onConnectTimeout?.Invoke();
        }

        private void OnConnectComplete()
        {
            if (_socket == null)
            {
                return;
            }
            TimerManager.ClearTimeout(_connectTimeoutID);
            if (_socket.Connected)
            {
                onConnectSuccess?.Invoke();
                // 开启线程处理数据包接收
                Thread receiveThread = new Thread(ReceiveData);
                receiveThread.IsBackground = true;
                receiveThread.Start();
                // 开启线程处理数据包发送
                Thread sendThread = new Thread(SendData);
                sendThread.IsBackground = true;
                sendThread.Start();
            }
            else
            {
                onConnectFailure?.Invoke();
            }
        }

        private void ReceiveData()
        {
            while (true)
            {
                Thread.Sleep(1);
                // 关闭连接后
                if (_socket == null || !_socket.Connected)
                {
                    break;
                }
                try
                {
                    // 没有可接收数据时等待
                    if (_socket.Available <= 0)
                    {
                        continue;
                    }
                    // 接收数据并解包
                    while (_packetParser.ReceiveData(_socket))
                    {
                        byte[] packetData = _packetParser.GetPacket();
                        MainThreadContext.Post(() =>
                        {
                            onReceiveData?.Invoke(packetData);
                        });
                    }
                    // 服务器是否主动断开连接
                    if (_packetParser.isConnectClosed)
                    {
                        Log.Warning($"[{_ip}:{_port}]服务端主动断开连接！");
                        MainThreadContext.Post((() =>
                        {
                            onConnectClosed?.Invoke();
                        }));
                        break;
                    }
                }
                catch (Exception e)
                {
                    Log.Warning($"[{_ip}:{_port}]数据接收错误: " + e);
                    MainThreadContext.Post((() =>
                    {
                        onReceiveError?.Invoke();
                    }));
                }
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        public void SendPacket(byte[] bytes)
        {
            if (_socket != null && _socket.Connected)
            {
                _sendQueue.Enqueue(bytes);
            }
        }

        private void SendData()
        {
            while (true)
            {
                Thread.Sleep(1);
                // 关闭连接后
                if (_socket == null || !_socket.Connected)
                {
                    break;
                }
                try
                {
                    while (_sendQueue.TryDequeue(out byte[] bytes))
                    {
                        if (BytesHeader(bytes, out byte[] headerBytes))
                        {
                            _socket.Send(headerBytes, SocketFlags.None);
                        }
                        _socket.Send(bytes, SocketFlags.None);
                    }
                }
                catch (Exception e)
                {
                    Log.Warning($"[{_ip}:{_port}]数据发送错误: " + e);
                    MainThreadContext.Post((() =>
                    {
                        onSendError?.Invoke();
                    }));
                }
            }
        }

        /// <summary>
        /// 写入数据头
        /// </summary>
        /// <param name="bytes">需要发送的数据</param>
        /// <param name="headerBytes">数据头数据</param>
        /// <returns>是否写入</returns>
        protected virtual bool BytesHeader(byte[] bytes, out byte[] headerBytes)
        {
            headerBytes = BitConverter.GetBytes((uint)bytes.Length);
            if (BitConverter.IsLittleEndian ^ _endian == Endian.LittleEndian)
            {
                headerBytes = headerBytes.Reverse().ToArray();
            }
            return true;
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            TimerManager.ClearTimeout(_connectTimeoutID);
            if (_socket != null && _socket.Connected)
            {
                _socket.Shutdown(SocketShutdown.Both);
                _socket.Close();
            }
            _socket = null;
            _packetParser = _createPacketParser();
            _sendQueue = new ConcurrentQueue<byte[]>();
        }
    }
}
